package sgsi.model.alocacion.estrategia;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import sgsi.model.alocacion.CriterioOrdenAreas;
import sgsi.model.alocacion.CriterioOrdenUbicacionesPorAjuste;
import sgsi.model.alocacion.EstadoEnum;
import sgsi.model.alocacion.ResultadoAlocacion;
import sgsi.model.entidades.Palet;
import sgsi.model.entidades.TipoDeArticulo;
import sgsi.model.entidades.TipoDePalet;
import sgsi.model.entidades.espacios.Almacen;
import sgsi.model.entidades.espacios.Area;
import sgsi.model.entidades.espacios.Ubicacion;

public class EstrategiaDeAlocacionImpl implements
		EstrategiaDeAlocacion {
	
	public ResultadoAlocacion ubicacionesSugeridas(List<Palet> palets, Almacen almacen, CriterioOrdenAreas criterio) {
		ResultadoAlocacion resultado = new ResultadoAlocacion();	
			
		List<Area> areas;
		TipoDeArticulo tipoDeArticulo = palets.get(0).getArticulo().getTipoDeArticulo();
		int paletsParaAlocar = palets.size();
		int paletActual = 0;
		
		if (palets.size() == 0)
			return new ResultadoAlocacion();
		
		//Paso 1: inicializo el criterio.
		criterio.initCriterio(palets);
		
		//Paso 2: filtro las areas, dejando aquellas que soporta la alocacion
		areas = filtrarAreas(almacen.getAreas(), tipoDeArticulo);
		
		//Paso 3: ordenar las Areas segun el criterio pasado por parametro
		Collections.sort(areas,  criterio);
		
		//Paso 4: comienzo a alocar
		Iterator<Area> iterarAreas = areas.iterator();
		while (iterarAreas.hasNext() && seguirAlocando(paletActual, paletsParaAlocar)) {
			Area area = iterarAreas.next();
			paletActual = alocarEnArea(area.getUbicaciones(), paletsParaAlocar, paletActual, palets);
		}		
		
		//Paso 5: seteo los resultados
		resultado.setPalets(palets);
		resultado.setPaletsAlocados(paletActual);
		resultado.setPaletsPendientes(paletsParaAlocar - paletActual);
		
		return resultado;
	}	
			
	private List<Area> filtrarAreas(Set<Area> areas, TipoDeArticulo tipoDeArticulo) {		
		List<Area> areasDisponibles = new ArrayList<Area>();
		
		Iterator<Area> iterarAreas = areas.iterator();
		while (iterarAreas.hasNext()) {
			Area area = iterarAreas.next();
			
			if (area.puedeAlmacenarA(tipoDeArticulo) && area.soportaAlArticulo(tipoDeArticulo))				
				areasDisponibles.add(area);	
		}
		
		return areasDisponibles;
	}
	
	private boolean seguirAlocando(int paletActual, int paletsParaAlocar) {
		return paletActual < paletsParaAlocar;
	}
	
	private int alocarEnArea(Set<Ubicacion> ubicacionesSinOrdenar, int paletsParaAlocar, int paletActual, List<Palet> palets) {
		CriterioOrdenUbicacionesPorAjuste criterio = new CriterioOrdenUbicacionesPorAjuste();
		criterio.initCriterio(palets);		
		
		List<Ubicacion> ubicacionesOrdenadas = criterio.ordenarUbicaciones(ubicacionesSinOrdenar);
		
		Iterator<Ubicacion> ubicacionesOrdernadasIterator = ubicacionesOrdenadas.iterator();		
		while (ubicacionesOrdernadasIterator.hasNext() && seguirAlocando(paletActual, paletsParaAlocar)) {
			Ubicacion ubicacion = ubicacionesOrdernadasIterator.next();
			paletActual = alocarEnUbicacion(ubicacion, paletsParaAlocar, paletActual, palets);									
		}
		
		return paletActual;
	}	
	
	private int alocarEnUbicacion(Ubicacion ubicacion, int paletsParaAlocar, int paletActual, List<Palet> palets) {
		int i = 0;
		
		float peso = palets.get(0).getPeso();
		float alto = palets.get(0).getAltura();
		TipoDePalet tipoDePalet = palets.get(0).getTipoDePalet();
		
		int cantidad = ubicacion.cantidadDisponiblePara(tipoDePalet, alto, peso);
		
		while (i < cantidad && seguirAlocando(paletActual, paletsParaAlocar)) {
			Palet palet = palets.get(paletActual);
			
			palet.setUbicacion(ubicacion);
			ubicacion.agregarPalet(palet);			
			palet.setEstado(EstadoEnum.ALOCADO.getEstado());
			
			paletActual ++;
			i++;
		}
		
		return paletActual;
	}
}
